package com.sh.data.engine.domain.annotation;


import com.sh.data.engine.common.constants.Constants;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.annotation.userlog.LogResult;
import com.sh.data.engine.domain.annotation.userlog.EngineOperation;
import com.sh.data.engine.domain.base.model.UserContext;
import com.sh.data.engine.domain.feign.FeignUser;
import com.sh.data.engine.domain.workspace.manager.model.domain.UserYunQiaoDomain;
import com.sh.data.engine.repository.dao.management.operation.OperationsLogMapper;
import com.sh.data.engine.repository.dao.management.operation.entity.OperationsLogEntity;
import com.sh.data.engine.repository.dao.workspace.index.ProjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;

@Aspect
@Component
public class LoggingAspect {

    @Autowired
    private OperationsLogMapper operationsLogMapper;

    @Autowired
    private FeignUser feignUser;

    @Autowired
    private ProjectMapper projectMapper;

    @Pointcut("@annotation(com.sh.data.engine.domain.annotation.LogExecution)")
    public void myLogExecution() {
    }

    public String getClientIpAddress(HttpServletRequest request) {
        // 定义需要检查的HTTP头列表
        String[] headers = {
                "X-Forwarded-For",
                "Proxy-Client-IP",
                "WL-Proxy-Client-IP",
                "HTTP_X_FORWARDED_FOR",
                "HTTP_CLIENT_IP"
        };

        String ipAddress = null;

        // 循环遍历所有的头以找到第一个非空、非未知的IP地址
        for (String header : headers) {
            ipAddress = request.getHeader(header);
            if (ipAddress != null && !ipAddress.isEmpty() && !"unknown".equalsIgnoreCase(ipAddress)) {
                break; // 找到有效的IP地址后退出循环
            }
        }

        // 如果没有找到，则使用request.getRemoteAddr()
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }

        // 处理X-Forwarded-For可能包含多个IP地址（由代理服务器添加）的情况
        if (ipAddress != null && ipAddress.contains(",")) {
            ipAddress = ipAddress.split(",")[0].trim(); // 取第一个IP并去除可能的前后空白字符
        }

        return ipAddress;
    }

    @Around("myLogExecution()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        LogExecution logExecution = signature.getMethod().getAnnotation(LogExecution.class);
        UserYunQiaoDomain user = feignUser.getMap(UserContext.getUserId());
        String userId = UserContext.getUserId();//操作人员
        String userType = user.getDisplayName();//用户类型
        String ipAddress = null;
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            //收集请求实例IP信息
            ipAddress = getClientIpAddress(request);
        }

        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        EngineOperation engineOperationLog = logExecution.operation();

        // 替换模板变量
        String detailInfo = logExecution.operation().getDetailInfoTemplate().replace("${businessName}",
                logExecution.business().getMenuCode()).replace("${name}", logExecution.business().getName());

//        String detailInfo = engineOperationLog.generateDetailInfo(businessName, name);//详细信息
        String operation = engineOperationLog.getCodeName();//操作
        String module = logExecution.business().getMenuCode();//所属模块
        String operationType = logExecution.business().getName();//操作类型
        LocalDateTime operationTime = LocalDateTime.now();//操作时间
        //如果没有空间ID，则使用默认值Constants.SYS_QUERY;
        String projectName = Constants.SYS_QUERY;
        if (ServletUtils.getProjectId() != null) {
            projectName = getProjectNameByProjectId(ServletUtils.getProjectId());
        }
        String operationResult = logExecution.result().getResult();//操作结果

        Object result;
        try {
            result = joinPoint.proceed(); // 继续执行方法
        } catch (Exception e) {
            // 记录异常日志
            operationResult = e.getMessage();
            saveLog(className, methodName, operation, operationType, module, projectName, detailInfo, operationTime, ipAddress, userType, userId, operationResult);
            throw e;
        }

        // 记录成功日志
        saveLog(className, methodName, operation, operationType, module, projectName, detailInfo, operationTime, ipAddress, userType, userId, operationResult);
        return result;
    }

    private void saveLog(String className, String methodName, String operation, String operationType, String module, String projectName, String detailInfo, LocalDateTime startTime, String IpAddress, String userType, String userName, String operationResult) {
        OperationsLogEntity log = new OperationsLogEntity();
        log.setOperationTime(startTime);
        log.setDetailInfo(detailInfo);
        log.setOperationType(operationType);
        log.setOperation(operation);
        log.setModule(module);
        log.setProjectName(projectName);
        log.setOperationResult(operationResult);
        log.setIpAddress(IpAddress);
        log.setUserType(userType);
        log.setOperator(userName);

        if (!operationResult.equals(LogResult.SUCCESS.getResult())) {

            log.setDetailInfo(log.getDetailInfo() + "," + (String.format("Class: %s, Method: %s, Error: %s", className, methodName, operationResult)));
        }

        operationsLogMapper.insert(log);
    }

    //根据空间ID获取空间名称
    private String getProjectNameByProjectId(Long projectId) {
        return projectMapper.selectById(projectId).getName();
    }
}